home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Think Class Libraries / CFlexiDataFile 1.1 / CFlexiDataFile.c next >
Encoding:
C/C++ Source or Header  |  1994-11-30  |  14.3 KB  |  761 lines  |  [TEXT/KAHL]

  1. /*
  2.  * CFlexiDataFile.c
  3.  *
  4.  * A subclass of CDataFile that can pretend that a resource,
  5.  * a handle, or a section of memory is actually a file on disk.
  6.  *
  7.  * © Copyright 1992 by Jamie R. McCarthy.  All rights reserved.
  8.  * This code can be both distributed and used freely.
  9.  * Internet: k044477@kzoo.edu            AppleLink: j.mccarthy
  10.  * Telephone:  800-421-4157 or US 616-665-7075 (9:00-5:00 Eastern time)
  11.  * I'm releasing this code with the hope that someone will get something
  12.  * out of it.  Feedback of any sort, even just letting me know that
  13.  * you're using it, is greatly appreciated!
  14.  *
  15.  * This version (Nov 92) doesn't support changing the size of
  16.  * resources.  Note that writing past the end of a resource will
  17.  * attempt to change its size (see WriteSome()).  As soon as
  18.  * JPartialResources gets that code written, this class will work.
  19.  * (Don't hold your breath.)
  20.  *
  21.  * CAVEAT HACKER:  the writing code (WriteSome() and especially
  22.  * WriteAll()) is completely untested.  Please don't make your
  23.  * un-backed-up master's thesis into my beta-test site;  step
  24.  * through the code with the debugger until you're confident that
  25.  * it works.  (And write me to let me know what you find.)
  26.  *
  27.  * CAVEAT HACKER:  using memory mode will give you a
  28.  * wonderful high-level object-oriented interface to something
  29.  * that's really as low-level as you can go.  What that means is,
  30.  * you can crash your machine in a truly spectacular fashion
  31.  * without even trying.  For example, it's a bad idea to assign
  32.  * a CFlexiDataFile to a section of memory in a handle that might
  33.  * move out from underneath it.  Another thing that you probably
  34.  * don't want to do, is to have WriteSome() or WriteAll() go past
  35.  * the end of your section of memory.  The code, as it stands,
  36.  * catches that and raises an exception.  If you actually do
  37.  * want to do that, change the #definition of the constant
  38.  * "allowWriteToImplicitlyChangeMemoryLength".
  39.  *
  40.  */
  41.  
  42.  
  43.  
  44. /********************************/
  45.  
  46. #include "CFlexiDataFile.h"
  47.  
  48. /********************************/
  49.  
  50. #include <TCLUtilities.h>
  51.  
  52. #include "JPartialResources.h"
  53.  
  54. /********************************/
  55.  
  56. enum {
  57.     kUnopenedDataMark = -1
  58. } ;
  59.  
  60. /********************************/
  61.  
  62. #define allowWriteToImplicitlyChangeMemoryLength (0)
  63.  
  64. /********************************/
  65.  
  66.  
  67.  
  68. void CFlexiDataFile::IFlexiDataFile(void)
  69. {
  70.     inherited::IDataFile();
  71.     
  72.     itsMode = kModeUnspecified;
  73.     itsDataHndl = NULL;
  74.     itsDataPointer = NULL;
  75.     itsDataMark = kUnopenedDataMark;
  76. }
  77.  
  78.  
  79.  
  80. void CFlexiDataFile::Dispose(void)
  81. {
  82.     inherited::Dispose();
  83. }
  84.  
  85.  
  86.  
  87. void CFlexiDataFile::Open(SignedByte permission)
  88. {
  89.     ASSERT(!IsOpen());
  90.     
  91.     switch (itsMode) {
  92.         
  93.         case kModeResource: {
  94.             Boolean oldResLoad;
  95.             oldResLoad = ResLoad;
  96.             SetResLoad(FALSE);
  97.             itsDataHndl = GetResource(itsRsrcType, itsRsrcID);
  98.             SetResLoad(oldResLoad);
  99.             if (itsDataHndl == NULL) {
  100.                 FailOSErr(resNotFound);
  101.             }
  102.             itsDataMark = 0;
  103.         }    break;
  104.             
  105.         case kModeHandle: {
  106.             ASSERT(itsDataHndl != NULL);
  107.             ASSERT(*itsDataHndl != NULL);
  108.             itsDataMark = 0;
  109.         }    break;
  110.             
  111.         case kModeMemory: {
  112.             ASSERT(itsDataPointer != NULL);
  113.             itsDataMark = 0;
  114.         }    break;
  115.             
  116.         case kModeDataFork: {
  117.             inherited::Open(permission);
  118.         }    break;
  119.             
  120.         default:
  121.             FailOSErr(paramErr);
  122.             break;
  123.             
  124.     }
  125.     
  126.     itsPermission = permission;
  127. }
  128.  
  129.  
  130.  
  131. void CFlexiDataFile::Close(void)
  132. {
  133.     if (!IsOpen()) return;
  134.     
  135.     switch (itsMode) {
  136.         
  137.         case kModeResource:
  138.             if (*itsDataHndl != NULL) {
  139.                 ForgetResource(itsDataHndl);
  140.             } else {
  141.                 DetachResource(itsDataHndl);
  142.                 DisposHandle(itsDataHndl);
  143.             }
  144.             itsDataHndl = NULL;
  145.             itsDataMark = kUnopenedDataMark;
  146.             break;
  147.             
  148.         case kModeHandle: {
  149.             HSetState(itsDataHndl, itsOldHState);
  150.             itsDataHndl = NULL;
  151.             itsDataMark = kUnopenedDataMark;
  152.         }     break;
  153.             
  154.         case kModeMemory: {
  155.             itsDataPointer = NULL;
  156.             itsDataMark = kUnopenedDataMark;
  157.         }     break;
  158.             
  159.         case kModeDataFork:
  160.             inherited::Close();
  161.             break;
  162.             
  163.         default:
  164.             break;
  165.             
  166.     }
  167. }
  168.  
  169.  
  170.  
  171. Boolean CFlexiDataFile::IsOpen(void)
  172. {
  173.     switch (itsMode) {
  174.         case kModeUnspecified:    return FALSE;                                            break;
  175.         case kModeDataFork:        return (refNum!=0);                                    break;
  176.         case kModeResource:
  177.         case kModeHandle:
  178.         case kModeMemory:            return (itsDataMark != kUnopenedDataMark);    break;
  179.         default:                        Debugger();                                                break;
  180.     }
  181. }
  182.  
  183.  
  184.  
  185. /********************************/
  186.  
  187.  
  188.  
  189. void CFlexiDataFile::Specify(Str63 aName, short aVolNum)
  190. {
  191.     ASSERT(!IsOpen());
  192.     itsMode = kModeDataFork;
  193.     inherited::Specify(aName, aVolNum);
  194. }
  195.  
  196.  
  197.  
  198. void CFlexiDataFile::SpecifyFSSpec(const FSSpec *aFileSpec)
  199. {
  200.     ASSERT(!IsOpen());
  201.     itsMode = kModeDataFork;
  202.     inherited::SpecifyFSSpec(aFileSpec);
  203. }
  204.  
  205.  
  206.  
  207. void CFlexiDataFile::SFSpecify(SFReply *macSFReply)
  208. {
  209.     ASSERT(!IsOpen());
  210.     
  211.         /*
  212.          * Here's the code that checks for the munged SFReply record.
  213.          */
  214.         
  215.     if (macSFReply->version == kResFileFlag) {
  216.         
  217.         rsrcSpecify(macSFReply->fType, macSFReply->vRefNum);
  218.         
  219.     } else if (macSFReply->version == kHndlFileFlag) {
  220.         
  221.         hndlSpecify((Handle) macSFReply->fType);
  222.         
  223.     } else if (macSFReply->version == kMemoryFileFlag) {
  224.         
  225.         memorySpecify((void*) macSFReply->fType, (unsigned short) macSFReply->vRefNum);
  226.         
  227.     } else {
  228.         
  229.         inherited::SFSpecify(macSFReply);
  230.         
  231.     }
  232. }
  233.  
  234.  
  235.  
  236. void CFlexiDataFile::ResolveFileAlias(void)
  237. {
  238.         /*
  239.          * Resources, handles, and sections of memory can't be aliases,
  240.          * so there's nothing to resolve.  However, if you want (e.g.)
  241.          * a resource to be able to resolve to (e.g.) a file on disk,
  242.          * all you have to do is override this method--rsrcSpecify(),
  243.          * hndlSpecify(), and memorySpecify() do indeed call
  244.          * ResolveFileAlias().
  245.          */
  246.     
  247.     if (itsMode == kModeDataFork) {
  248.         inherited::ResolveFileAlias();
  249.     }
  250. }
  251.  
  252.  
  253.  
  254. void CFlexiDataFile::rsrcSpecify(ResType theRsrcType, short theRsrcID)
  255. {
  256.     ASSERT(!IsOpen());
  257.     
  258.     itsMode = kModeResource;
  259.     
  260.         /*
  261.          * The handle will be loaded in when the file is opened.
  262.          */
  263.         
  264.     itsDataHndl = NULL;
  265.     itsDataMark = kUnopenedDataMark;
  266.     
  267.     itsRsrcType = theRsrcType;
  268.     itsRsrcID = theRsrcID;
  269.     
  270.     ResolveFileAlias();
  271. }
  272.  
  273.  
  274.  
  275. void CFlexiDataFile::hndlSpecify(Handle theHndl)
  276. {
  277.     ASSERT(!IsOpen());
  278.     
  279.     itsMode = kModeHandle;
  280.     
  281.     itsDataHndl = theHndl;
  282.     itsDataMark = kUnopenedDataMark;
  283.     
  284.     ASSERT(theHndl != NULL && *theHndl != NULL);
  285.     itsOldHState = HGetState(theHndl);
  286.     HNoPurge(theHndl);
  287.     
  288.     ResolveFileAlias();
  289. }
  290.  
  291.  
  292.  
  293. void CFlexiDataFile::memorySpecify(void *thePointer, long theLength)
  294. {
  295.     ASSERT(!IsOpen());
  296.     
  297.     itsMode = kModeMemory;
  298.     
  299.     itsDataPointer = thePointer;
  300.     itsDataLength = theLength;
  301.     itsDataMark = kUnopenedDataMark;
  302.     
  303.     ASSERT(thePointer != NULL);
  304.     
  305.     ResolveFileAlias();
  306. }
  307.  
  308.  
  309.  
  310. /********************************/
  311.  
  312.  
  313.  
  314. void CFlexiDataFile::GetName(Str63 theName)
  315. {
  316.     switch (itsMode) {
  317.         
  318.         case kModeResource: {
  319.             short dummyID;
  320.             ResType dummyResType;
  321.             ASSERT(itsDataHndl != NULL);
  322.             GetResInfo(itsDataHndl, &dummyID, &dummyResType, theName);
  323.         } break;
  324.             
  325.         case kModeHandle:
  326.             theName[0] = 0;        // Handles don't have names
  327.             break;
  328.             
  329.         case kModeMemory:
  330.             theName[0] = 0;        // Sections of memory don't have names
  331.             break;
  332.             
  333.         default:
  334.             inherited::GetName(theName);
  335.             break;
  336.             
  337.     }
  338. }
  339.  
  340.  
  341.  
  342. void CFlexiDataFile::GetFSSpec(FSSpec *aFileSpec)
  343. {
  344.     if (itsMode == kModeDataFork) {
  345.         inherited::GetFSSpec(aFileSpec);
  346.     } else {
  347.         Failure(paramErr, 0);
  348.     }
  349. }
  350.  
  351.  
  352.  
  353. /********************************/
  354.  
  355.  
  356.  
  357. void CFlexiDataFile::CreateNew(OSType creator, OSType fType)
  358. {
  359.         /* See comments in the .h file on this method and ThrowOut(). */
  360.     ASSERT(itsMode == kModeDataFork);
  361.     inherited::CreateNew(creator, fType);
  362. }
  363.  
  364.  
  365.  
  366. void CFlexiDataFile::ThrowOut(void)
  367. {
  368.         /*
  369.          * Apparently, HDelete() moves memory--or at least, it has for me.
  370.          * So the object needs to be locked down when it's called.  Here's
  371.          * as good a place as any to do it.
  372.          */
  373.         
  374.     Boolean wasLocked;
  375.     ASSERT(itsMode == kModeDataFork);
  376.     wasLocked = Lock(TRUE);
  377.     inherited::ThrowOut();
  378.     Lock(wasLocked);
  379. }
  380.  
  381.  
  382.  
  383. /********************************/
  384.  
  385.  
  386.  
  387. void CFlexiDataFile::ChangeName(Str63 newName)
  388. {
  389.     if (itsMode == kModeDataFork) {
  390.         inherited::ChangeName(newName);
  391.     } else {
  392.         Failure(paramErr, 0);
  393.     }
  394. }
  395.  
  396.  
  397.  
  398. Boolean CFlexiDataFile::ExistsOnDisk(void)
  399. {
  400.     if (itsMode == kModeDataFork) {
  401.         return inherited::ExistsOnDisk();
  402.     } else {
  403.         Failure(paramErr, 0);
  404.     }
  405. }
  406.  
  407.  
  408.  
  409. void CFlexiDataFile::GetMacFileInfo(FInfo *fileInfo)
  410. {
  411.     if (itsMode == kModeDataFork) {
  412.         inherited::GetMacFileInfo(fileInfo);
  413.     } else {
  414.         Failure(paramErr, 0);
  415.     }
  416. }
  417.  
  418.  
  419.  
  420. /********************************/
  421.  
  422.  
  423.  
  424. void CFlexiDataFile::SetLength(long aLength)
  425. {
  426.     ASSERT(IsOpen());
  427.     
  428.     testWritePermission();
  429.     
  430.     switch (itsMode) {
  431.         
  432.         case kModeResource:
  433.             jSetResourceSize(itsDataHndl, aLength);
  434.             FailResError();
  435.             break;
  436.             
  437.         case kModeHandle:
  438.             SetHandleSize(itsDataHndl, aLength);
  439.             FailMemError();
  440.             break;
  441.             
  442.         case kModeMemory:
  443.             itsDataLength = aLength;
  444.             testMemoryValidity();
  445.             break;
  446.             
  447.         default:
  448.             inherited::SetLength(aLength);
  449.             break;
  450.             
  451.     }
  452. }
  453.  
  454.  
  455.  
  456. long CFlexiDataFile::GetLength(void)
  457. {
  458.     ASSERT(IsOpen());
  459.     
  460.     switch (itsMode) {
  461.         
  462.         case kModeResource:
  463.             return SizeResource(itsDataHndl);
  464.             break;
  465.             
  466.         case kModeHandle:
  467.             return GetHandleSize(itsDataHndl);
  468.             break;
  469.             
  470.         case kModeMemory:
  471.             return itsDataLength;
  472.             break;
  473.             
  474.         default:
  475.             return inherited::GetLength();
  476.             break;
  477.             
  478.     }
  479. }
  480.  
  481.  
  482.  
  483. void CFlexiDataFile::SetMark(long howFar, short fromWhere)
  484. {
  485.     ASSERT(IsOpen());
  486.     
  487.     switch (itsMode) {
  488.         
  489.         case kModeResource:
  490.         case kModeHandle:
  491.         case kModeMemory:
  492.             switch (fromWhere) {
  493.                 case fsFromStart:        itsDataMark = howFar;                        break;
  494.                 case fsFromLEOF:        itsDataMark = GetLength() - howFar;        break;
  495.                 case fsFromMark:        itsDataMark += howFar;                        break;
  496.                 default:                    FailOSErr(paramErr);                            break;
  497.             }
  498.             break;
  499.             
  500.         default:
  501.             inherited::SetMark(howFar, fromWhere);
  502.             break;
  503.             
  504.     }
  505. }
  506.  
  507.  
  508.  
  509. long CFlexiDataFile::GetMark(void)
  510. {
  511.     ASSERT(IsOpen());
  512.     
  513.     switch (itsMode) {
  514.         
  515.         case kModeResource:
  516.         case kModeHandle:
  517.         case kModeMemory:
  518.             return itsDataMark;
  519.             break;
  520.             
  521.         default:
  522.             inherited::GetMark();
  523.             break;
  524.             
  525.     }
  526. }
  527.  
  528.  
  529.  
  530. Handle CFlexiDataFile::ReadAll(void)
  531. {
  532.     ASSERT(IsOpen());
  533.     
  534.     switch (itsMode) {
  535.         
  536.         case kModeResource: {
  537.             
  538.             short oldResLoad;
  539.             Handle theReturnHndl;
  540.             
  541.             oldResLoad = ResLoad;
  542.             theReturnHndl = itsDataHndl;
  543.             LoadResource(theReturnHndl);
  544.             DetachResource(theReturnHndl);
  545.             
  546.             SetResLoad(FALSE);
  547.             itsDataHndl = GetResource(itsRsrcType, itsRsrcID);
  548.             SetResLoad(oldResLoad);
  549.             
  550.             return theReturnHndl;
  551.             
  552.         }    break;
  553.             
  554.         case kModeHandle: {
  555.             
  556.             Handle copyOfDataHndl;
  557.             copyOfDataHndl = itsDataHndl;
  558.             HandToHand(©OfDataHndl);
  559.             return copyOfDataHndl;
  560.             
  561.         }    break;
  562.             
  563.         case kModeMemory: {
  564.             
  565.             Handle copyOfData;
  566.             copyOfData = NewHandle(itsDataLength);
  567.             if (copyOfData != NULL) {
  568.                 BlockMove(itsDataPointer, *copyOfData, itsDataLength);
  569.             }
  570.             return copyOfData;
  571.             
  572.         }    break;
  573.             
  574.         default:
  575.             return inherited::ReadAll();
  576.             break;
  577.             
  578.     }
  579. }
  580.  
  581.  
  582.  
  583. void CFlexiDataFile::ReadSome(Ptr info, long howMuch)
  584. {
  585.     ASSERT(IsOpen());
  586.     
  587.     switch (itsMode) {
  588.         
  589.         case kModeResource: {
  590.             ASSERT(itsDataMark + howMuch <= GetLength());
  591.             jReadPartialResource(itsDataHndl, itsDataMark, info, howMuch);
  592.             itsDataMark += howMuch;
  593.         }    break;
  594.             
  595.         case kModeHandle: {
  596.             ASSERT(itsDataMark + howMuch <= GetLength());
  597.             BlockMove(*itsDataHndl + itsDataMark, info, howMuch);
  598.             itsDataMark += howMuch;
  599.         }    break;
  600.             
  601.         case kModeMemory: {
  602.             ASSERT(itsDataMark + howMuch <= itsDataLength);
  603.             BlockMove((Ptr) itsDataPointer + itsDataMark, info, howMuch);
  604.             itsDataMark += howMuch;
  605.         }    break;
  606.             
  607.         default:
  608.             inherited::ReadSome(info, howMuch);
  609.             break;
  610.             
  611.     }
  612. }
  613.  
  614.  
  615.  
  616. void CFlexiDataFile::WriteAll(Handle contents)
  617. {
  618.     ASSERT(IsOpen());
  619.     
  620.     testWritePermission();
  621.     
  622.     switch (itsMode) {
  623.         
  624.         case kModeResource: {
  625.             Str255 theName;
  626.             ASSERT(itsDataHndl != NULL);
  627.             SetMark(0, fsFromStart);
  628.             GetName(theName);
  629.             RmveResource(itsDataHndl);
  630.             ForgetHandle(itsDataHndl);
  631.             TRY {
  632.                 HandToHand(&contents);
  633.                 FailMemError();
  634.                 TRY {
  635.                     AddResource(contents, itsRsrcType, itsRsrcID, theName);
  636.                     FailResError();
  637.                     itsDataHndl = contents;
  638.                 } CATCH {
  639.                         // couldn't add the resource--forget the allocation
  640.                     ForgetHandle(contents);
  641.                 } ENDTRY;
  642.             } CATCH {
  643.                     // couldn't write--re-open the file
  644.                 Open(fsRdWrPerm);
  645.             } ENDTRY;
  646.         }    break;
  647.             
  648.         case kModeHandle:
  649.             ForgetHandle(itsDataHndl);
  650.             TRY {
  651.                 HandToHand(&contents);
  652.                 FailMemError();
  653.                 itsDataHndl = contents;
  654.             } CATCH {
  655.                     // couldn't dupe the handle--close the file.
  656.                 itsDataMark = kUnopenedDataMark;
  657.             } ENDTRY;
  658.             break;
  659.             
  660.         case kModeMemory:
  661. #if allowWriteToImplicitlyChangeMemoryLength
  662.             SetLength(GetHandleSize(contents));
  663. #else
  664.             ASSERT(GetLength() == GetHandleSize(contents));
  665. #endif
  666.             SetMark(0, fsFromStart);
  667.             BlockMove(*contents, itsDataPointer, itsDataLength);
  668.             break;
  669.             
  670.         default:
  671.             inherited::WriteAll(contents);
  672.             break;
  673.             
  674.     }
  675. }
  676.  
  677.  
  678.  
  679. void CFlexiDataFile::WriteSome(Ptr info, long howMuch)
  680. {
  681.     ASSERT(IsOpen());
  682.     
  683.     testWritePermission();
  684.     
  685.         /*
  686.          * This is necessary for writing to resources and handles, so
  687.          * you don't write data over a resource map or BlockMove() past
  688.          * the end of a handle.  (Well, actually, jWritePartialResource
  689.          * will call jSetResourceSize for you, but calling it here
  690.          * doesn't hurt.  And it is necessary for kModeHandle.  And
  691.          * jSetResourceSize doesn't work yet anyway, so all this is
  692.          * hypothetical discussion, sigh...)
  693.          *
  694.          * As far as I know, this has no effect on writing to disk:
  695.          * FSWrite sets the eof as far ahead as it needs to, right?
  696.          */
  697.     
  698. #if allowWriteToImplicitlyChangeMemoryLength
  699.     if (itsMode == kModeMemory) {
  700.         ASSERT(GetMark() + howMuch <= GetLength());
  701.     }
  702. #endif
  703.     
  704.     if (GetMark() + howMuch > GetLength()) {
  705.         SetLength(GetMark() + howMuch);
  706.     }
  707.     ASSERT(GetMark() + howMuch <= GetLength());
  708.     
  709.     switch (itsMode) {
  710.         
  711.         case kModeResource:
  712.             jWritePartialResource(itsDataHndl, itsDataMark, info, howMuch);
  713.             itsDataMark += howMuch;
  714.             break;
  715.             
  716.         case kModeHandle:
  717.             BlockMove(info, *itsDataHndl + itsDataMark, howMuch);
  718.             itsDataMark += howMuch;
  719.             break;
  720.             
  721.         case kModeMemory:
  722.             BlockMove(info, (Ptr) itsDataPointer + itsDataMark, howMuch);
  723.             itsDataMark += howMuch;
  724.             break;
  725.             
  726.         default:
  727.             inherited::WriteSome(info, howMuch);
  728.             break;
  729.             
  730.     }
  731. }
  732.  
  733.  
  734.  
  735. /********************************/
  736.  
  737.  
  738.  
  739. short CFlexiDataFile::getMode(void)
  740. {
  741.     return itsMode;
  742. }
  743.  
  744.  
  745.  
  746. void CFlexiDataFile::testWritePermission(void)
  747. {
  748.     if (itsPermission != fsRdWrPerm) {
  749.         FailOSErr(wrPermErr);
  750.     }
  751. }
  752.  
  753.  
  754.  
  755. void CFlexiDataFile::testMemoryValidity(void)
  756. {
  757.     // I don't really know what to do here...
  758.     ASSERT(itsDataPointer <= (void*) 0x20000000);
  759.     ASSERT((Ptr) itsDataPointer + itsDataMark <= (Ptr) 0x20000000);
  760. }
  761.